package com.yami.shop.search.common.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.yami.shop.bean.bo.ProductBO;
import com.yami.shop.bean.enums.EsOperationType;
import com.yami.shop.bean.enums.ProdStatusEnums;
import com.yami.shop.bean.event.EsMysteryBoxEvent;
import com.yami.shop.bean.event.EsProductEvent;
import com.yami.shop.bean.vo.EsMysteryBoxUpdateVO;
import com.yami.shop.bean.vo.EsProdUpdateVO;
import com.yami.shop.dao.ProductMapper;
import com.yami.shop.search.common.constant.EsIndexEnum;
import com.yami.shop.search.common.service.EsMysteryBoxService;
import com.yami.shop.search.common.util.EsSearchProdUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wxf
 * @date 2023/6/2 16:44
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class EsMysteryBoxServiceImpl implements EsMysteryBoxService {

    private final ApplicationEventPublisher eventPublisher;
    private final ProductMapper productMapper;
    private static String INDEXIDPREFIX = "box";

    @Override
    public void save(Long prodId) {
        ProductBO productBO = getProductBo(prodId);
        if (Objects.equals(productBO.getStatus(), ProdStatusEnums.DELETE.getValue())) {
            return;
        }
        productBO.setAppDisplay(true);
        productBO.setIndexIdPrefix(INDEXIDPREFIX);
        EsSearchProdUtil.save(productBO.getProdId(), productBO);
    }

    @Override
    public void saveBatch(List<Long> prodIds) {
        List<ProductBO> productBOList = getProductBo(prodIds);
        if (CollUtil.isEmpty(productBOList)) {
            return;
        }
        productBOList.forEach(s->{s.setIndexIdPrefix(INDEXIDPREFIX);s.setAppDisplay(true);});
        Map<Long, ProductBO> map = productBOList.stream()
                .filter(productBO -> !Objects.equals(productBO.getStatus(), ProdStatusEnums.DELETE.getValue()))
                .collect(Collectors.toMap(ProductBO::getProdId, p -> p));
        EsSearchProdUtil.saveBatch(map);
    }

    @Override
    public void update(Long prodId) {
        if (Objects.isNull(prodId)) {
            return;
        }
        ProductBO productBO = getProductBo(prodId);
        productBO.setAppDisplay(true);
        if (Objects.equals(productBO.getStatus(), ProdStatusEnums.DELETE.getValue())) {
            delete(prodId);
            return;
        }
        productBO.setAppDisplay(true);
        productBO.setIndexIdPrefix(INDEXIDPREFIX);
        EsSearchProdUtil.update(productBO);
    }

    @Override
    public void updateBatch(List<Long> prodIds) {
        if (CollUtil.isEmpty(prodIds)) {
            return;
        }
        List<ProductBO> productBOList = getProductBo(prodIds);
        Map<Long, ProductBO> map = new HashMap<>(productBOList.size());
        for (ProductBO productBO : productBOList) {
            productBO.setAppDisplay(true);
            productBO.setIndexIdPrefix(INDEXIDPREFIX);
            map.put(productBO.getProdId(), productBO);
        }
        EsSearchProdUtil.updateBatch(map);
    }

    @Override
    public void updateByCategoryId(Long categoryId) {
        if (Objects.isNull(categoryId)) {
            return;
        }
        List<Long> ids = productMapper.listProdId(categoryId, null, null);
        updateBatch(ids);
    }

    @Override
    public void updateByShopCategoryId(Long shopCategoryId) {
        if (Objects.isNull(shopCategoryId)) {
            return;
        }
        List<Long> ids = productMapper.listProdId(null, shopCategoryId, null);
        updateBatch(ids);
    }

    @Override
    public void updateSoldNum(Long prodId) {
        if (Objects.isNull(prodId)) {
            return ;
        }
        ProductBO productBO = getProductBo(prodId, EsOperationType.UPDATE_SOLD_NUM);
        productBO.setAppDisplay(true);
        productBO.setIndexIdPrefix(INDEXIDPREFIX);
        EsSearchProdUtil.partialUpdate(productBO);
    }
    @Override
    public void updateSoldNumBatch(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return ;
        }
        List<ProductBO> productList = getProductBo(ids, EsOperationType.UPDATE_SOLD_NUM);
        for (ProductBO productBO : productList) {
            productBO.setAppDisplay(true);
            productBO.setIndexIdPrefix(INDEXIDPREFIX);
            EsSearchProdUtil.partialUpdate(productBO);
        }
    }

    @Override
    public void updateByShopId(Long shopId) {
        if (Objects.isNull(shopId)) {
            return;
        }
        List<Long> ids = productMapper.listProdId(null, null, shopId);
        updateBatch(ids);
    }

    @Override
    public void delete(Long prodId) {
        if (Objects.isNull(prodId)) {
            return;
        }
        EsSearchProdUtil.delete(INDEXIDPREFIX + String.valueOf(prodId));
    }

    @Override
    public void deleteBatch(List<Long> ids) {
        List<String> strs = new ArrayList<>();
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        ids.forEach(s->{
            strs.add(INDEXIDPREFIX + s);
        });
        EsSearchProdUtil.deleteBatch(strs);
    }

    @Override
    public void changeProdStockBatch(List<EsMysteryBoxUpdateVO> prodList) {
        if (CollUtil.isEmpty(prodList)) {
            return;
        }
        Map<String, Script> map = new HashMap<>(prodList.size());
        for (EsMysteryBoxUpdateVO esProdUpdateVO : prodList) {
            String code = "-= ";
            if (Objects.equals(esProdUpdateVO.getType(), 1)) {
                code = "+= ";
            }
            Script script = new Script(ScriptType.INLINE,
                    "painless",
                    "ctx._source.totalStocks " + code + esProdUpdateVO.getCount(),
                    Collections.emptyMap());
            map.put(INDEXIDPREFIX + esProdUpdateVO.getBoxId(), script);
        }
        EsSearchProdUtil.updateStockByPainless(map);
    }

    @Override
    public void changeProdSoldBatch(List<EsMysteryBoxUpdateVO> prodList) {
        if (CollUtil.isEmpty(prodList)) {
            return;
        }
        Map<String, Script> map = new HashMap<>(prodList.size());
        for (EsMysteryBoxUpdateVO esProdUpdateVO : prodList) {
            String code = "-= ";
            if (Objects.equals(esProdUpdateVO.getType(), 1)) {
                code = "+= ";
            }
            code = "ctx._source.soldNum " + code + esProdUpdateVO.getCount() + ";" + "ctx._source.actualSoldNum " + code + esProdUpdateVO.getCount();
            Script script = new Script(ScriptType.INLINE,
                    "painless",
                    code,
                    Collections.emptyMap());
            map.put(INDEXIDPREFIX + esProdUpdateVO.getBoxId(), script);
        }
        EsSearchProdUtil.updateStockByPainless(map);
    }

    /**
     * 获取盲盒商品信息
     * @param prodId 盲盒商品id
     * @return
     */
    private ProductBO getProductBo(Long prodId) {
        if (Objects.isNull(prodId)) {
            return null;
        }
        List<ProductBO> productList = getProductBo(Collections.singletonList(prodId));
        if (CollUtil.isEmpty(productList)) {
            return null;
        }
        return productList.get(0);
    }

    /**
     * 获取盲盒商品信息
     * @param prodId 盲盒商品id
     * @return
     */
    private ProductBO getProductBo(Long prodId, EsOperationType operationType) {
        if (Objects.isNull(prodId)) {
            return null;
        }
        List<ProductBO> productList = getProductBo(Collections.singletonList(prodId), operationType);
        if (CollUtil.isEmpty(productList)) {
            return null;
        }
        return productList.get(0);
    }

    /**
     * 获取盲盒商品信息
     * @param prodIds 盲盒商品id
     * @return
     */
    private List<ProductBO> getProductBo(List<Long> prodIds) {
        return listProductBo(prodIds, null);
    }

    /**
     * 获取盲盒商品信息
     * @param prodIds 盲盒商品id
     * @return
     */
    private List<ProductBO> getProductBo(List<Long> prodIds, EsOperationType operationType) {
        return listProductBo(prodIds, operationType);
    }

    /**
     * 获取盲盒商品信息
     * @param prodIds 盲盒商品id列表
     * @return
     */
    private List<ProductBO> listProductBo(List<Long> prodIds, EsOperationType operationType) {
        if (CollUtil.isEmpty(prodIds)) {
            return null;
        }
        List<ProductBO> productList = new ArrayList<>();
        try {
            eventPublisher.publishEvent(new EsMysteryBoxEvent(prodIds, productList, operationType));
        } catch (Exception e) {
            log.error("获取es盲盒商品数据异常：{}", e);
        }
        if (CollUtil.isEmpty(productList) || Objects.isNull(productList.get(0))) {
            log.error("盲盒商品id为：{}, type:{}的盲盒商品数据异常！", prodIds, operationType.value());
            return null;
        }
        return productList;
    }

}
